#include "Utils.h"
#include "Resources.h"

static __Dictionary* i18n = NULL;

__Dictionary* Utils::getI18NInstance() {
    if (i18n == NULL) {
        i18n = __Dictionary::create();
        CC_SAFE_RETAIN(i18n);
    }
    return i18n;
}

Utils::Utils() {
}

Utils::~Utils() {
}

MenuItemSprite* Utils::createButton(char* normalFrame, char* selectedFrame, char* text, int fontSize, char* fontType, Color3B color) {
    char* normalFrameName = (char*) "";
    normalFrameName = normalFrame;
    char* selectedFrameName = (char*) "";
    selectedFrameName = selectedFrame;

    Sprite* normalSprite = Sprite::createWithSpriteFrameName(normalFrameName);
    Sprite* selectedSprite = Sprite::createWithSpriteFrameName(selectedFrameName);
    MenuItemSprite* button = MenuItemSprite::create(normalSprite, selectedSprite);
    
    Label* normalText = Utils::createLabel(text,fontType, fontSize);
    normalText->setColor(color);
    normalText->setDimensions(normalText->getContentSize().width + 20, 0);
    normalText->setPosition(Point(button->getContentSize().width / 2, button->getContentSize().height / 2+3));
    normalText->enableShadow();
    normalText->setTag(1);
    normalSprite->addChild(normalText);
    
    Label* selectedText = Utils::createLabel(text,fontType,fontSize);
    selectedText->setColor(color);
    selectedText->setDimensions(selectedText->getContentSize().width + 20, 0);
    selectedText->setPosition(Point(button->getContentSize().width / 2, button->getContentSize().height / 2));
    selectedText->enableShadow();
    selectedText->setTag(1);
    selectedSprite->addChild(selectedText);
    
    return button;
}

MenuItemSprite* Utils::createButton(char* spriteFrameName) {
    Sprite* normalSprite = Sprite::createWithSpriteFrameName(spriteFrameName);
    MenuItemSprite* button = MenuItemSprite::create(normalSprite, normalSprite);
    return button;
}

MenuItemSprite* Utils::createButton(char* normalSpriteFrameName, char* selectedSpriteFrameName, bool flipX) {
    Sprite* normalSprite = Sprite::createWithSpriteFrameName(normalSpriteFrameName);
    normalSprite->setFlippedX(flipX);
    Sprite* selectedSprite = Sprite::createWithSpriteFrameName(selectedSpriteFrameName);
    selectedSprite->setFlippedX(flipX);
    MenuItemSprite* button = MenuItemSprite::create(normalSprite, selectedSprite);
    
    return button;
}

MenuItemSprite* Utils::createButton(char* normalSpriteFrameName, char* selectedSpriteFrameName) {
    return Utils::createButton(normalSpriteFrameName, selectedSpriteFrameName, false);
}

MenuItemSprite* Utils::createButton(char* normalSpriteFrameName, char* selectedSpriteFrameName, char* disabledSpriteFrameName) {
    Sprite* normalSprite = Sprite::createWithSpriteFrameName(normalSpriteFrameName);
    Sprite* selectedSprite = Sprite::createWithSpriteFrameName(selectedSpriteFrameName);
    Sprite* disabledSprite = Sprite::createWithSpriteFrameName(disabledSpriteFrameName);
    MenuItemSprite* button = MenuItemSprite::create(normalSprite, selectedSprite, disabledSprite);
    return button;
}

Label* Utils::createLabel(char* text, int fontSize) {
    Label* label = Label::createWithSystemFont(text, (char*) FONT_ARIAL, fontSize);
    label->setHorizontalAlignment(TextHAlignment::CENTER);
    label->setDimensions(label->getContentSize().width + 20, 0);
    return label;
}

Label* Utils::createLabel(char* text, char* fontName, int fontSize) {
    Label* label = Label::createWithTTF(text, fontName, fontSize);
    label->setHorizontalAlignment(TextHAlignment::CENTER);
    label->setDimensions(label->getContentSize().width + 20, 0);
    return label;
}

float Utils::random() {
    float random = (float) rand() / static_cast<double>(RAND_MAX);
    return random;
}

bool Utils::b2Interact(Body *body1, Body *body2) {
    if (!body1->isPhysical() || !body2->isPhysical()) {
        return false;
    }
    
    b2Transform s1Transform, s2Transform;
    s1Transform.Set(b2Vec2(body1->getPosition().x, body1->getPosition().y), body1->getRotation() * 3.14 / 180);
    s2Transform.Set(b2Vec2(body2->getPosition().x, body2->getPosition().y), body2->getRotation() * 3.14 / 180);
    
    b2Manifold manifold;
    
    bool body1ShapeType = body1->isRect();
    bool body2ShapeType = body2->isRect();
    
    if (body1ShapeType && !body2ShapeType) {
        b2PolygonShape shape1;
        RectBody* shape1Body = (RectBody*) body1;
        shape1.SetAsBox(shape1Body->getWidth() / 2, shape1Body->getHeight() / 2);
        
        b2CircleShape shape2;
        ArcBody* shape2Body = (ArcBody*) body2;
        shape2.m_radius = shape2Body->getRadius();
        
        b2CollidePolygonAndCircle(&manifold, &shape1, s1Transform, &shape2, s2Transform);
    } else if (!body1ShapeType && body2ShapeType) {
        b2CircleShape shape1;
        ArcBody* shape1Body = (ArcBody*) body1;
        shape1.m_radius = shape1Body->getRadius();
        
        b2PolygonShape shape2;
        RectBody* shape2Body = (RectBody*) body2;
        shape2.SetAsBox(shape2Body->getWidth() / 2, shape2Body->getHeight() / 2);
        
        b2CollidePolygonAndCircle(&manifold, &shape2, s2Transform, &shape1, s1Transform);
    } else if (!body1ShapeType && !body2ShapeType) {
        b2CircleShape shape1;
        ArcBody* shape1Body = (ArcBody*) body1;
        shape1.m_radius = shape1Body->getRadius();
        
        b2CircleShape shape2;
        ArcBody* shape2Body = (ArcBody*) body2;
        shape2.m_radius = shape2Body->getRadius();
        
        b2CollideCircles(&manifold, &shape1, s1Transform, &shape2, s2Transform);
    } else if (body1ShapeType && body2ShapeType) {
        b2PolygonShape shape1;
        RectBody* shape1Body = (RectBody*) body1;
        shape1.SetAsBox(shape1Body->getWidth() / 2, shape1Body->getHeight() / 2);
        
        b2PolygonShape shape2;
        RectBody* shape2Body = (RectBody*) body2;
        shape2.SetAsBox(shape2Body->getWidth() / 2, shape2Body->getHeight() / 2);
        
        b2CollidePolygons(&manifold, &shape1, s1Transform, &shape2, s2Transform);
    }
    
    if (manifold.pointCount > 0) {
        return true;
    }
    
    return false;
}

std::string Utils::readFile(std::string fileName, bool writeable, bool override, bool encrypt) {
    std::string path = "";
    
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    path = FileUtils::getInstance()->getWritablePath() + fileName;
    FILE* file = fopen(path.c_str(), "rb");
    if (file == NULL || override) {
        ssize_t fileSize = 0;
        unsigned char* pBuffer = FileUtils::getInstance()->getFileData(fileName.c_str(), "rb", &fileSize);
        
        FILE *fpw = fopen(path.c_str(), "wb");
        fwrite(pBuffer, 1, fileSize, fpw);
        fclose(fpw);
    }
    if (file != NULL) {
        fclose(file);
    }
#endif
    
#if (CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID)
    if (writeable) {
        path = FileUtils::getInstance()->getWritablePath() + fileName;
    } else {
        path = FileUtils::getInstance()->fullPathForFilename(fileName.c_str());
    }
#endif
    
	FILE *fp = fopen(path.c_str(), "rb");
    char* allStringData = NULL;
    
	if (fp != NULL) {
        fseek(fp, 0, SEEK_END);
        int fileLength = (int)ftell(fp);
        allStringData = (char*)std::malloc(fileLength * sizeof(char) + 1);
        fseek(fp, SEEK_SET, 0);
        fread(allStringData, fileLength, 1, fp);
        allStringData[fileLength] = '\0';
        fclose(fp);
	}
    
    if (allStringData == NULL) {
        return "";
    }
    
    if (encrypt) {
        std::string decryptData = Utils::decrypt(allStringData);
        return decryptData;
    }
    return allStringData;
}


void Utils::writeFile(std::string data, std::string fileName, bool encrypt) {
    std::string path = "";
    
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    path = FileUtils::getInstance()->getWritablePath() + fileName;
#endif
    
#if (CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID && CC_TARGET_PLATFORM != CC_PLATFORM_IOS)
    path = FileUtils::sharedFileUtils()->fullPathForFilename(fileName.c_str());
#endif
    
    FILE *fp = fopen(path.c_str(), "wb");
    std::string dataToWrite = data;
    if (encrypt) {
        dataToWrite = Utils::encrypt(dataToWrite.data());
    }
    
    if (fp != NULL) {
        fwrite(dataToWrite.data(), sizeof(char), dataToWrite.length(), fp);
        fclose(fp);
    }
}

bool Utils::checkPointInRect(RectBody* rectBody, Point point) {
    if (point.x >= rectBody->getPosition().x - rectBody->getWidth() / 2
        && point.x <= rectBody->getPosition().x + rectBody->getWidth() / 2
        && point.y >= rectBody->getPosition().y - rectBody->getHeight() / 2
        && point.y <= rectBody->getPosition().y + rectBody->getHeight() / 2) {
		return true;
	}
	return false;
}

bool Utils::checkPointInArc(ArcBody* arcBody, Point point) {
    float offX = point.x - arcBody->getPosition().x;
	float offY =  point.y - arcBody->getPosition().y;
    
	float distance = sqrt(offX * offX + offY * offY);
    
	if (distance < arcBody->getRadius()) {
		return true;
	}
	return false;
}

std::string Utils::convertTimeString(int remainTime) {
    if (remainTime <= 0) {
        return "00:00";
    }
	int minute = remainTime / 60;
	int second = remainTime % 60;
	std::string minuteString = "";
	std::string secondString = "";
    
	std::ostringstream minuteStream;
	minuteStream << minute;
	minuteString = minuteStream.str();
    
	std::ostringstream secondStream;
	secondStream << second;
	secondString = secondStream.str();
    
	if (minute < 10) {
		minuteString = "0" + minuteString;
	}
	if (second < 10) {
		secondString = "0" + secondString;
	}
    
	char timeString[128];
	sprintf(timeString, "%s:%s", minuteString.c_str(), secondString.c_str());
    return timeString;
}

void Utils::safeRelease(Ref* object) {
    if (object != NULL) {
        if (object->getReferenceCount() > 0) {
            CC_SAFE_RELEASE(object);
        }
    }
}

long Utils::getCurrentTimeInSeconds() {
    time_t timer;
    time(&timer);
    return timer;
}

__Array* Utils::split(const std::string &s, char delim) {
    __Array* result = __Array::create();
    std::stringstream ss(s);
    std::string item;
    while (getline(ss, item, delim)) {
        result->addObject(CCString::create(item));
    }
    return result;
}

std::string Utils::encrypt(std::string toEncrypt) {
    std::string keyStr = UG_KEY;
    
    std::string output = toEncrypt;
    for (int i = 0; i < keyStr.length(); i++) {
        char key = keyStr[i];
        for (int j = 0; j < toEncrypt.size(); j++) {
            output[j] = toEncrypt[j] ^ key;
        }
        toEncrypt = output;
    }
    return output;
}

std::string Utils::encryptWithKey(std::string toEncrypt, std::string keyStr) {
    std::string output = toEncrypt;
    for (int i = 0; i < keyStr.length(); i++) {
        char key = keyStr[i];
        for (int j = 0; j < toEncrypt.size(); j++) {
            output[j] = toEncrypt[j] ^ key;
        }
        toEncrypt = output;
    }
    return output;
    
}

std::string Utils::decrypt(std::string toDecript) {
    std::string keyStr = UG_KEY;
    
    std::string output = toDecript;
    for (int i = keyStr.length() - 1; i >= 0; i--) {
        char key = keyStr[i];
        for (int j = 0; j < toDecript.size(); j++) {
            output[j] = toDecript[j] ^ key;
        }
        toDecript = output;
    }
    return output;
}

long Utils::adler32(char* data, int32_t len) {
    const int MOD_ADLER = 65521;
    uint32_t a = 1, b = 0;
    int32_t index;
    
    /* Process each byte of the data in order */
    for (index = 0; index < len; ++index) {
        a = (a + data[index]) % MOD_ADLER;
        b = (b + a) % MOD_ADLER;
    }
    
    return (b << 16) | a;
}

static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

std::string Utils::base64_encode(char const* bytes_to_encode, unsigned int in_len) {
    std::string ret;
    int i = 0;
    int j = 0;
    unsigned char char_array_3[3];
    unsigned char char_array_4[4];
    
    while (in_len--) {
        char_array_3[i++] = *(bytes_to_encode++);
        if (i == 3) {
            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
            char_array_4[3] = char_array_3[2] & 0x3f;
            
            for(i = 0; (i <4) ; i++)
                ret += base64_chars[char_array_4[i]];
            i = 0;
        }
    }
    
    if (i) {
        for(j = i; j < 3; j++)
            char_array_3[j] = '\0';
        
        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
        char_array_4[3] = char_array_3[2] & 0x3f;
        
        for (j = 0; (j < i + 1); j++)
            ret += base64_chars[char_array_4[j]];
        
        while((i++ < 3))
            ret += '=';
        
    }
    
    return ret;
}

static inline bool is_base64(unsigned char c) {
    return (isalnum(c) || (c == '+') || (c == '/'));
}

std::string Utils::base64_decode(std::string const& encoded_string) {
    size_t in_len = encoded_string.size();
    size_t i = 0;
    size_t j = 0;
    int in_ = 0;
    unsigned char char_array_4[4], char_array_3[3];
    std::string ret;
    
    while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
        char_array_4[i++] = encoded_string[in_]; in_++;
        if (i ==4) {
            for (i = 0; i <4; i++)
                char_array_4[i] = static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
            
            char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
            char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
            char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
            
            for (i = 0; (i < 3); i++)
                ret += char_array_3[i];
            i = 0;
        }
    }
    
    if (i) {
        for (j = i; j <4; j++)
            char_array_4[j] = 0;
        
        for (j = 0; j <4; j++)
            char_array_4[j] = static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
        
        char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
        char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
        char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
        
        for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
    }
    
    return ret;
}

float Utils::getTimeFromJsonSpine(std::string JsonFile, std::string name) {
    float finallyTime = 0;
    Json* root;
    std::string fileData;
    fileData = Utils::readFile(JsonFile, false, true, false);
    root = Json_create(fileData.data());
    if (root) {
        Json* animations = Json_getItem(root, "animations");
        Json* animation = Json_getItem(animations,name.data());
        Json* bones = Json_getItem(animation,"bones");
        Json* boneChildren = bones->child;
        for (Json* temp = boneChildren; temp->next != NULL; temp = temp->next) {
            Json* translate = Json_getItem(temp, "translate");
            int size = translate->size;
            Json* translateChildren = Json_getItemAt(translate, size - 1);
            float time = Json_getFloat(translateChildren, "time", 0);
            if (time > finallyTime) {
                finallyTime = time;
            }
        }
        return finallyTime;
    } else {
        return 0;
    }
}






